# src\core\str_utils.py
import secrets
import ast
from datetime import datetime, timedelta
import time

import zoneinfo
import uuid
from typing import List, Dict, Any, Optional, Sequence
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError

# 创建全局 PasswordHasher 实例
# 创建全局 PasswordHasher 实例，使用自定义参数
ph = PasswordHasher(
    time_cost=3,  # 迭代次数 t=3
    memory_cost=65536,  # 内存使用量 m=65536 KiB (64 MB)
    parallelism=4,  # 并行度 p=4
    hash_len=32,  # 哈希长度 32 字节
    salt_len=16  # 盐值长度 16 字节
)


def secret_key(length=16):
    return secrets.token_hex(length)  # 参数 32 表示字节，hex 编码后是 64 字符


def get_password_hash(password: str) -> str:
    """使用 Argon2 生成密码哈希"""
    if password is None:
        raise ValueError("Password cannot be None")
    """返回盐值和哈希值的元组"""
    full_hash = ph.hash(password)
    # 分割字符串获取盐值和哈希值
    parts = full_hash.split('$')
    salt = parts[4]
    hash_value = parts[5]
    return f"{salt}${hash_value}"


def verify_password(plain_password: str, hash_value: str) -> bool:
    """使用分离的盐值和哈希值验证密码"""
    # 重新构造完整哈希字符串
    # 动态获取 ph 实例的配置参数
    time_cost = ph.time_cost
    memory_cost = ph.memory_cost
    parallelism = ph.parallelism
    TypeID = 'argon2id'
    version = '19'

    # 重新构造完整哈希字符串
    full_hash = f"${TypeID}$v={version}$m={memory_cost},t={time_cost},p={parallelism}${hash_value}"

    # full_hash = hash_value
    try:
        ph.verify(full_hash, plain_password)
        return True
    except VerifyMismatchError:
        return False


def convert_to_int(value, default=0):
    try:
        return int(value)  # ast.literal_eval(value)
    except (ValueError, SyntaxError):
        return default


def convert_bool(value):
    """
    将字符串 "True" 和 "False" 转换为布尔值 True 和 False，
    或将布尔值 True 和 False 转换为字符串 "True" 和 "False"。

    参数:
        value: 输入值，可以是字符串 "True" 或 "False"，也可以是布尔值 True 或 False。

    返回:
        转换后的值。
    """
    if isinstance(value, str):  # 如果输入是字符串
        if value == "True":
            return True
        elif value == "False":
            return False
        else:
            raise ValueError(f"无效的字符串值：{value}")
    elif isinstance(value, bool):  # 如果输入是布尔值
        return "True" if value else "False"
    else:
        raise TypeError(f"不支持的输入类型：{type(value)}")


def tranDict(data):
    """
    从给定的数据中提取特定键对应的值，如果该值是一个非空列表且列表中的元素是字典，
    则直接返回这个列表；如果不是这种情况，则将整个数据包装成一个列表并返回。
    :param data: 输入的数据，可以是字典等类型。
    :param key: 要提取的键，默认为'columns'。
    :return: 符合特定条件的列表。
    """
    extracted_lists = data
    if extracted_lists:
        if isinstance(extracted_lists, list) and len(extracted_lists) > 0 and isinstance(extracted_lists[0], dict):
            pass
        else:
            extracted_lists = [extracted_lists]
    else:
        extracted_lists = []
    return extracted_lists


def DictConvert(data):
    if isinstance(data, list):
        if len(data) == 1 and isinstance(data[0], dict):
            return data[0]
        else:
            return None
    else:
        return None


def model_to_dict(model_instance):
    """
    将SQLAlchemy模型实例转换为字典

    Args:
        model_instance: SQLAlchemy模型实例

    Returns:
        dict: 包含模型数据的字典
    """

    if model_instance is None:
        return None
        # 如果是列表，递归处理每个元素
    if isinstance(model_instance, list):
        return [model_to_dict(item) for item in model_instance]
        # 2. 已经是 dict，直接处理日期字段即可
        if isinstance(model_instance, dict):
            result = {}
            for k, v in model_instance.items():
                if hasattr(v, 'isoformat'):  # 处理日期
                    result[k] = v.isoformat()
                else:
                    result[k] = v
            return result

    # 获取模型实例的__dict__并过滤掉SQLAlchemy内部属性
    result = {}
    for key, value in model_instance.__dict__.items():
        # 过滤掉SQLAlchemy内部属性（以下划线开头的属性）
        if not key.startswith('_'):
            # 处理日期时间对象
            if hasattr(value, 'isoformat'):
                result[key] = value.isoformat()
            else:
                result[key] = value

    return result


def models_to_dict(model_list: List) -> List[Dict[str, Any]]:
    """
    列表 SQLAlchemy 模型 → 字典列表
    """
    if not model_list:
        return []
    return [model_to_dict(item) for item in model_list]


def model_to_list(models: Optional[Sequence]) -> List[Dict[str, Any]]:
    """
    将 SQLAlchemy 模型实例或模型实例列表统一转换为字典列表。

    Args:
        models: 单条 SQLAlchemy 模型实例 或 模型实例列表/查询结果。
                允许 None、单条、列表、Query 等可迭代对象。

    Returns:
        List[Dict[str, Any]]: 始终返回字典列表；空输入返回空列表。
    """
    if models is None:
        return []

    # 如果是单条模型，包装成列表处理
    if not isinstance(models, (list, tuple, set)) or hasattr(models, "_sa_instance_state"):
        models = [models]

    result: List[Dict[str, Any]] = []
    for item in models:
        if item is None:
            continue
        # 过滤 SQLAlchemy 内部属性
        row = {
            key: (val.isoformat() if hasattr(val, "isoformat") else val)
            for key, val in item.__dict__.items()
            if not key.startswith("_")
        }
        result.append(row)
    return result


def timestamp_to_date(timestamp: int, timezone_offset: int = None) -> str:
    """
    将时间戳转换为具体的日期和时间字符串。

    参数:
        timestamp (int): 时间戳（秒）
        timezone_offset (int): 时区偏移量（小时）。默认为0（UTC）。

    返回:
        str: 格式化的日期和时间字符串（YYYY-MM-DD HH:MM:SS）
    # 示例用法
    timestamp = 1742958426
    print(timestamp_to_date(timestamp))  # 默认为 UTC 时间
    print(timestamp_to_date(timestamp, timezone_offset=8))  # 转换为 UTC+8 时间
    """
    # 将时间戳转换为 datetime 对象（UTC 时间）
    utc_time = datetime.utcfromtimestamp(timestamp)
    # 获取当前时区偏移量（默认为 操作系统时区）
    if not timezone_offset:
        timezone_offset = time.localtime().tm_gmtoff / 3600
    # 如果有时区偏移量，调整时间
    if timezone_offset:
        utc_time += timedelta(hours=timezone_offset)

    # 格式化日期和时间
    formatted_date = utc_time.strftime('%Y-%m-%d %H:%M:%S')
    return formatted_date


def reverse(s):
    """
    反转字符串中的字符顺序
    :param s: 要反转的字符串
    :return: 反转后的字符串
    """
    return s[::-1]


def reverse_words(s):
    """
    反转字符串中每个单词的字符顺序，但保持单词之间的顺序不变
    :param s: 要处理的字符串
    :return: 处理后的字符串
    """
    # 将字符串分割成单词列表
    words = s.split()
    # 对每个单词进行字符反转
    reversed_words = [word[::-1] for word in words]
    # 将反转后的单词用空格连接起来
    return ' '.join(reversed_words)


def is_empty_or_whitespace(input_string):
    """
    判断一个字符串是否为空或者只包含空格。

    :param input_string: 要判断的字符串
    :return: 如果字符串为空或只包含空格，返回True；否则返回False。
    """
    # 使用strip()方法移除字符串两端的空格，然后判断结果是否为空
    return not input_string.strip()


def is_not_empty(value):
    # 检查是否为 None
    if value is None:
        return False

        # 检查是否为空字符串（但不包括空格）
    if isinstance(value, str) and not value.strip():
        return False

        # 检查是否为其他空的数据结构（如空列表、空字典等）
    if not value:
        return False

        # 如果上述条件都不满足，则认为值不为空
    return True


def is_empty(value):
    # 检查是否为 None
    if value is None:
        return True

        # 检查是否为空字符串（但不包括空格）
    if isinstance(value, str) and not value.strip():
        return True

        # 检查是否为其他空的数据结构（如空列表、空字典等）
    if not value:
        return True

        # 如果上述条件都不满足，则认为值不为空
    return False


def is_number(value):
    if is_empty(value):
        return False
    if is_dictionary(value):
        return False

    # 尝试将值转换为整数
    try:
        int(value)
        return True
    except ValueError:
        pass

        # 尝试将值转换为浮点数
    try:
        float(value)
        return True
    except ValueError:
        pass

        # 如果以上都失败，则值不是数字
    return False


def is_date(date_string, date_format='%Y-%m-%d'):
    """
    判断给定的字符串是否是有效的日期。

    :param date_string: 要检查的日期字符串
    :param date_format: 日期的格式，默认为 '%Y-%m-%d'
    :return: 如果字符串是有效的日期则返回 True，否则返回 False
    """
    if (is_empty(date_string)):
        return False
    if (is_dictionary(date_string)):
        return False

    try:
        datetime.strptime(date_string, date_format)
        return True
    except ValueError:
        return False


def is_dictionary(obj):
    """
    判断给定的对象是否是字典。
    """
    return isinstance(obj, dict)


def has_timezone(dt):
    """
    此函数用于检查给定的 datetime 对象是否具有时区信息。

    参数:
    dt (datetime.datetime): 要检查的 datetime 对象

    返回:
    bool: 如果 datetime 对象具有时区信息则返回 True，否则返回 False
    """
    return dt.tzinfo is not None


def without_timezone(dt):
    """
        此函数用于去除给定的 datetime 对象中的时区信息。

        参数:
        dt (datetime.datetime): 要处理的 datetime 对象

        返回:
        datetime.datetime: 去除时区信息后的 datetime 对象，如果输入对象原本就没有时区信息，则直接返回
        """
    if has_timezone(dt):
        dt = dt.replace(tzinfo=None)
    return dt


def generate_id():
    return str(uuid.uuid4())


def now():
    return datetime.now()


def now_date():
    return datetime.now().date()


def tranList(data, key='details'):
    """
    从给定的数据中提取特定键对应的值，如果该值是一个非空列表且列表中的元素是字典，
    则直接返回这个列表；如果不是这种情况，则将整个数据包装成一个列表并返回。
    :param data: 输入的数据，可以是字典等类型。
    :param key: 要提取的键，默认为'columns'。
    :return: 符合特定条件的列表。
    """
    extracted_lists = None
    if key in data and data[key]:
        extracted_lists = data[key]

    if extracted_lists:
        if isinstance(extracted_lists, list) and len(extracted_lists) > 0 and isinstance(extracted_lists[0], dict):
            pass
        else:
            extracted_lists = [extracted_lists]
    else:
        extracted_lists = []
    return extracted_lists


if __name__ == '__main__':
    # secret_key()
    try:
        # 使用较短的测试密码
        test_password = 'B1234'
        result = get_password_hash(test_password)
        print("哈希生成成功:", result)  # 只显示前20个字符
        print("\n--- 验证测试 ---")
        if verify_password(test_password, result):
            print("密码验证成功")

    except Exception as e:
        print(f"测试失败: {e}")
        # 添加新测试

    """print(time.tzname[0])
    print(time.localtime().tm_gmtoff / 3600)
    print(timestamp_to_date(1742958426))

    


       # 获取上海时区
       shanghai_tz = pytz.timezone('Asia/Shanghai')
       now1 = datetime.now(shanghai_tz)
       now2 = datetime.now()
       print(now1)
       print(now2)
       print(now())


       input_time = '2024-07-30 14:03:46.830311+08:00'
       time_with_tz = datetime.fromisoformat(input_time)

       # 获取上海时区
       # 获取上海时区
       shanghai_tz = zoneinfo.ZoneInfo('Asia/Shanghai')

       # 将时间转换为上海时区
       time_in_shanghai = time_with_tz.astimezone(shanghai_tz)
       print(time_in_shanghai)
       formatted_now = time_in_shanghai.strftime("%Y-%m-%d %H:%M:%S")
       print(formatted_now)
      """
    """

    original_string = "Hello, World!"
    original_string = "sen_shan@139.com"
    reversed_string = reverse(original_string)
    print("Original:", original_string)
    print("Reversed:", reversed_string)
    original_string = "Hello World This Is A Test"
    original_string = "sen_shan@139.com"
    reversed_string = reverse_words(original_string)
    print("Original:", original_string)
    print("Reversed:", reversed_string)
    print(is_empty_or_whitespace(" "))
    print("is_not_empty")
    print(is_not_empty(None))  # False
    print(is_not_empty(''))  # False
    print(is_not_empty(' '))  # True，因为空格不是空值
    print(is_not_empty('Hello'))  # True
    print(is_not_empty([]))  # False
    print(is_not_empty({}))  # False
    print(is_not_empty([1, 2, 3]))  # True
    print(is_not_empty({'key': 'value'}))  # True
    print("is_empty")
    print(is_empty(None))  # True
    print(is_empty(''))  # True
    print(is_empty(' '))  # False，因为空格不是空值
    print(is_empty('Hello'))  # False
    print(is_empty([]))  # True
    print(is_empty({}))  # True
    print(is_empty([1, 2, 3]))  # False
    print(is_empty({'key': 'value'}))  # False
    print("is_number")
    print(is_number(123))  # True
    print(is_number(123.45))  # True
    print(is_number('123'))  # True
    print(is_number('123.45'))  # True
    print(is_number('abc'))  # False
    print(is_number(None))  # False
    print(is_number([]))  # False
    print(is_number({'key': 'value'}))  # False

    print("is_date")
    print(is_date('2023-03-15'))  # True，假设默认的日期格式为 '%Y-%m-%d'
    print(is_date('15-03-2023', '%d-%m-%Y'))  # True，使用自定义的日期格式
    print(is_date('2023/13/35'))  # False，月份和日期无效
    print(is_date('not a date'))  # False，不是日期的字符串
    """

    # 测试函数
